package com.tencent.cloud.uikit.floatwindow;

import android.animation.TimeInterpolator;
import android.content.Context;
import android.os.Handler;
import android.support.annotation.LayoutRes;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;

import com.tencent.cloud.uikit.utils.ScreenUtil;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by yhao on 2017/12/22.
 * https://github.com/yhaolpz
 */

public class FloatWindow {

    private static final String mDefaultTag = "default_float_window_tag";

    private static Map<String, IFloatWindow> sFloatWindowMap;

    private static Context sContext;

    private static Handler sHandler;

    private FloatWindow() {
    }

    public static void init(Context context) {
        sContext = context.getApplicationContext();
        sHandler = new Handler();
    }

    public static IFloatWindow get() {
        return get(mDefaultTag);
    }

    public static IFloatWindow get(@NonNull String tag) {
        return sFloatWindowMap == null ? null : sFloatWindowMap.get(tag);
    }

    @MainThread
    public static Builder builder() {
        return new Builder();
    }

    @MainThread
    public static Builder buildFixedWindow() {
        return new Builder(MoveType.fixed);
    }

    public static void destroy() {
        destroy(mDefaultTag);
    }

    public static void destroy(String tag) {
        if (sFloatWindowMap == null || !sFloatWindowMap.containsKey(tag)) {
            return;
        }
        IFloatWindow floatWindow = sFloatWindowMap.get(tag);
        floatWindow.dismiss();
        sFloatWindowMap.remove(tag);
        floatWindow.destroy();
    }

    public static void hide() {
        sFloatWindowMap.get(mDefaultTag).hide();
    }

    public static void show() {
        sFloatWindowMap.get(mDefaultTag).show();
    }

    public static class Builder {

        /**
         * 进程上下文
         */
        Context mApplicationContext;

        /**
         * 悬浮窗唯一标识
         */
        private String mTag = mDefaultTag;

        /**
         * 悬浮窗视图
         */
        View mView;
        private int mLayoutId;
        int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
        int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
        int gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
        int xOffset;
        int yOffset;
        boolean mShow = true;

        /**
         * Activity子类，非空时表示仅能在此数组中Activity Class显示浮窗
         */
        Class[] mActivities;

        /**
         * 浮窗移动模式
         */
        int mMoveType = MoveType.slide;
        int mSlideLeftMargin;
        int mSlideRightMargin;
        long mDuration = 300;
        TimeInterpolator mInterpolator;

        /**
         * 是否支持进程切后台后，桌面也显示浮窗
         */
        boolean mDesktopShow = false;

        /**
         * 权限监听器
         */
        PermissionListener mPermissionListener;

        /**
         * 视图状态监听器
         */
        ViewStateListener mViewStateListener;

        private Builder() {
            mApplicationContext = sContext;
//            setWidth(Screen.width, 1.0f)
//                    .setHeight(Screen.width, 0.1f)
//                    .setX(Screen.width, 0.0f)
//                    .setY(Screen.height, 0.0f);
        }

        private Builder(@MoveType.MOVE_TYPE int moveType) {
            this();
            mMoveType = moveType;
            if (MoveType.fixed == mMoveType) {
                mDuration = 0;
            }
        }

        public Builder setView(@NonNull View view) {
            mView = view;
            return this;
        }

        public Builder setView(@LayoutRes int layoutId) {
            mLayoutId = layoutId;
            return this;
        }

        public Builder setWidth(int width) {
            mWidth = width;
            return this;
        }

        public Builder setHeight(int height) {
            mHeight = height;
            return this;
        }

        public Builder setWidth(@Screen.screenType int screenType, float ratio) {
            mWidth = (int) ((screenType == Screen.width ?
                    ScreenUtil.getScreenWidth(sContext) :
                    ScreenUtil.getScreenHeight(sContext)) * ratio);
            return this;
        }

        public Builder setHeight(@Screen.screenType int screenType, float ratio) {
            mHeight = (int) ((screenType == Screen.width ?
                    ScreenUtil.getScreenWidth(sContext) :
                    ScreenUtil.getScreenHeight(sContext)) * ratio);
            return this;
        }

        public Builder setX(int x) {
            xOffset = x;
            return this;
        }

        public Builder setY(int y) {
            yOffset = y;
            return this;
        }

        public Builder setX(@Screen.screenType int screenType, float ratio) {
            xOffset = (int) ((screenType == Screen.width ?
                    ScreenUtil.getScreenWidth(sContext) :
                    ScreenUtil.getScreenHeight(sContext)) * ratio);
            return this;
        }

        public Builder setY(@Screen.screenType int screenType, float ratio) {
            yOffset = (int) ((screenType == Screen.width ?
                    ScreenUtil.getScreenWidth(sContext) :
                    ScreenUtil.getScreenHeight(sContext)) * ratio);
            return this;
        }

        /**
         * 设置 Activity 过滤器，用于指定在哪些界面显示悬浮窗，默认全部界面都显示
         *
         * @param show       　过滤类型,子类类型也会生效
         * @param activities 　过滤界面
         */
        public Builder setFilter(boolean show, @NonNull Class... activities) {
            mShow = show;
            mActivities = activities;
            return this;
        }

        public Builder setMoveType(@MoveType.MOVE_TYPE int moveType) {
            return setMoveType(moveType, 0, 0);
        }

        /**
         * 设置带边距的贴边动画，只有 moveType 为 MoveType.slide，设置边距才有意义，这个方法不标准，后面调整
         *
         * @param moveType         贴边动画 MoveType.slide
         * @param slideLeftMargin  贴边动画左边距，默认为 0
         * @param slideRightMargin 贴边动画右边距，默认为 0
         */
        public Builder setMoveType(@MoveType.MOVE_TYPE int moveType, int slideLeftMargin, int
                slideRightMargin) {
            mMoveType = moveType;
            mSlideLeftMargin = slideLeftMargin;
            mSlideRightMargin = slideRightMargin;
            if (MoveType.fixed == moveType) {
                mDuration = 0;
            }
            return this;
        }

        public Builder setMoveStyle(long duration, @Nullable TimeInterpolator interpolator) {
            mDuration = duration;
            mInterpolator = interpolator;
            return this;
        }

        public Builder setDuration(long duration) {
            mDuration = duration;
            return this;
        }

        public Builder setTag(@NonNull String tag) {
            mTag = tag;
            return this;
        }

        public String getTag() {
            return mTag;
        }

        public Builder setDesktopShow(boolean show) {
            mDesktopShow = show;
            return this;
        }

        public Builder setPermissionListener(PermissionListener listener) {
            mPermissionListener = listener;
            return this;
        }

        public Builder setViewStateListener(ViewStateListener listener) {
            mViewStateListener = listener;
            return this;
        }

        public void build() {
            if (sFloatWindowMap == null) {
                synchronized (FloatWindow.class) {
                    if (sFloatWindowMap == null) {
                        sFloatWindowMap = new ConcurrentHashMap<>();
                    }
                }
            }

            if (sFloatWindowMap.containsKey(mTag)) {
                Log.e("FloatWindow", "sFloatWindowMap.containsKey(mTag) : " + mTag);
                FloatWindow.destroy(mTag);
//                throw new IllegalArgumentException("FloatWindow of this tag has been added, " +
//                        "Please set a new tag for the new FloatWindow");
            }

            if (mView == null && mLayoutId == 0) {
                throw new IllegalArgumentException("View has not been set!");
            }

            if (mView == null) {
                mView = ScreenUtil.inflate(sContext, mLayoutId);
            }

            IFloatWindow floatWindowImpl = new IFloatWindowImpl(this);
            sFloatWindowMap.put(mTag, floatWindowImpl);
        }

        public void dismissDelayed() {
            if (MoveType.fixed == mMoveType && mDuration > 0) {
                sHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        FloatWindow.destroy(mTag);
                    }
                }, mDuration);
            }
        }
    }
}
